error_log("Converting from $src_encoding to $dest_encoding: not supported...");
}
return $escaped_data;
}
/// xml parser handler function for opening element tags
function xmlrpc_se($parser, $name, $attrs, $accept_single_vals=false)
{
// if invalid xmlrpc already detected, skip all processing
if ($GLOBALS['_xh']['isf'] < 2)
{
// check for correct element nesting
// top level element can only be of 2 types
/// @todo optimization creep: save this check into a bool variable, instead of using count() every time:
/// there is only a single top level element in xml anyway
if (count($GLOBALS['_xh']['stack']) == 0)
{
if ($name != 'METHODRESPONSE' && $name != 'METHODCALL' && (
$name != 'VALUE' && !$accept_single_vals))
{
$GLOBALS['_xh']['isf'] = 2;
$GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element';
return;
}
else
{
$GLOBALS['_xh']['rt'] = strtolower($name);
}
}
else
{
// not top level element: see if parent is OK
$parent = end($GLOBALS['_xh']['stack']);
if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name]))
{
$GLOBALS['_xh']['isf'] = 2;
$GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent";
return;
}
}
switch($name)
{
// optimize for speed switch cases: most common cases first
case 'VALUE':
/// @todo we could check for 2 VALUE elements inside a MEMBER or PARAM element
$GLOBALS['_xh']['vt']='value'; // indicator: no value found yet
$GLOBALS['_xh']['ac']='';
$GLOBALS['_xh']['lv']=1;
$GLOBALS['_xh']['php_class']=null;
break;
case 'I4':
case 'INT':
case 'STRING':
case 'BOOLEAN':
case 'DOUBLE':
case 'DATETIME.ISO8601':
case 'BASE64':
if ($GLOBALS['_xh']['vt']!='value')
{
//two data elements inside a value: an error occurred!
$GLOBALS['_xh']['isf'] = 2;
$GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
return;
}
$GLOBALS['_xh']['ac']=''; // reset the accumulator
break;
case 'STRUCT':
case 'ARRAY':
if ($GLOBALS['_xh']['vt']!='value')
{
//two data elements inside a value: an error occurred!
$GLOBALS['_xh']['isf'] = 2;
$GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
return;
}
// create an empty array to hold child values, and push it onto appropriate stack
$cur_val = array();
$cur_val['values'] = array();
$cur_val['type'] = $name;
// check for out-of-band information to rebuild php objs
// and in case it is found, save it
if (@isset($attrs['PHP_CLASS']))
{
$cur_val['php_class'] = $attrs['PHP_CLASS'];
}
$GLOBALS['_xh']['valuestack'][] = $cur_val;
$GLOBALS['_xh']['vt']='data'; // be prepared for a data element next
break;
case 'DATA':
if ($GLOBALS['_xh']['vt']!='data')
{
//two data elements inside a value: an error occurred!
$GLOBALS['_xh']['isf'] = 2;
$GLOBALS['_xh']['isf_reason'] = "found two data elements inside an array element";
return;
}
case 'METHODCALL':
case 'METHODRESPONSE':
case 'PARAMS':
// valid elements that add little to processing
break;
case 'METHODNAME':
case 'NAME':
/// @todo we could check for 2 NAME elements inside a MEMBER element
$GLOBALS['_xh']['ac']='';
break;
case 'FAULT':
$GLOBALS['_xh']['isf']=1;
break;
case 'MEMBER':
$GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name']=''; // set member name to null, in case we do not find in the xml later on
//$GLOBALS['_xh']['ac']='';
// Drop trough intentionally
case 'PARAM':
// clear value type, so we can check later if no value has been passed for this param/member
$GLOBALS['_xh']['vt']=null;
break;
case 'NIL':
if ($GLOBALS['xmlrpc_null_extension'])
{
if ($GLOBALS['_xh']['vt']!='value')
{
//two data elements inside a value: an error occurred!
$GLOBALS['_xh']['isf'] = 2;
$GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
return;
}
$GLOBALS['_xh']['ac']=''; // reset the accumulator
break;
}
// we do not support the <NIL/> extension, so
// drop through intentionally
default:
/// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
$GLOBALS['_xh']['isf'] = 2;
$GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name";
break;
}
// Save current element name to stack, to validate nesting
$GLOBALS['_xh']['stack'][] = $name;
/// @todo optimization creep: move this inside the big switch() above
if($name!='VALUE')
{
$GLOBALS['_xh']['lv']=0;
}
}
}
/// Used in decoding xml chunks that might represent single xmlrpc values
function xmlrpc_se_any($parser, $name, $attrs)
{
xmlrpc_se($parser, $name, $attrs, true);
}
/// xml parser handler function for close element tags
function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true)
{
if ($GLOBALS['_xh']['isf'] < 2)
{
// push this element name from stack
// NB: if XML validates, correct opening/closing is guaranteed and
// we do not have to check for $name == $curr_elem.
// we also checked for proper nesting at start of elements...
$curr_elem = array_pop($GLOBALS['_xh']['stack']);
switch($name)
{
case 'VALUE':
// This if() detects if no scalar was inside <VALUE></VALUE>
if ($GLOBALS['_xh']['vt']=='value')
{
$GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
$GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString'];
}
if ($rebuild_xmlrpcvals)
{
// build the xmlrpc val out of the data received, and substitute it
$temp =& new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']);
// in case we got info about underlying php class, save it
// in the object we're rebuilding
if (isset($GLOBALS['_xh']['php_class']))
$temp->_php_class = $GLOBALS['_xh']['php_class'];
// check if we are inside an array or struct:
// if value just built is inside an array, let's move it into array on the stack
$vscount = count($GLOBALS['_xh']['valuestack']);
if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
// user did not declare type of response value: try to guess it
if (is_object($this->val) && is_a($this->val, 'xmlrpcval'))
{
$this->valtyp = 'xmlrpcvals';
}
else if (is_string($this->val))
{
$this->valtyp = 'xml';
}
else
{
$this->valtyp = 'phpvals';
}
}
else
{
// user declares type of resp value: believe him
$this->valtyp = $valtyp;
}
}
}
/**
* Returns the error code of the response.
* @return integer the error code of this response (0 for not-error responses)
* @access public
*/
function faultCode()
{
return $this->errno;
}
/**
* Returns the error code of the response.
* @return string the error string of this response ('' for not-error responses)
* @access public
*/
function faultString()
{
return $this->errstr;
}
/**
* Returns the value received by the server.
* @return mixed the xmlrpcval object returned by the server. Might be an xml string or php value if the response has been created by specially configured xmlrpc_client objects
* @access public
*/
function value()
{
return $this->val;
}
/**
* Returns an array with the cookies received from the server.
* Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
* with attributes being e.g. 'expires', 'path', domain'.
* NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
* are still present in the array. It is up to the user-defined code to decide
* how to use the received cookies, and wheter they have to be sent back with the next
* request to the server (using xmlrpc_client::setCookie) or not
* @return array array of cookies received from the server
* @access public
*/
function cookies()
{
return $this->_cookies;
}
/**
* Returns xml representation of the response. XML prologue not included
* @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
* @return string the xml representation of the response
error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to decode the deflated data received from server');
$r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
return $r;
}
}
else
{
error_log('XML-RPC: xmlrpcmsg::parseResponse: the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
$r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
return $r;
}
}
}
} // end of 'if needed, de-chunk, re-inflate response'
// real stupid hack to avoid PHP 4 complaining about returning NULL by ref
$r = null;
$r =& $r;
return $r;
}
/**
* Parse the xmlrpc response contained in the string $data and return an xmlrpcresp object.
* @param string $data the xmlrpc response, eventually including http headers
* @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and consequent decoding
* @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
* @return xmlrpcresp
* @access public
*/
function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals')
{
if($this->debug)
{
//by maHo, replaced htmlspecialchars with htmlentities